package cn.aitrox.ry.app.web.service.impl;

import cn.aitrox.ry.app.web.config.AppNacosProperties;
import cn.aitrox.ry.app.web.service.AnnoResultService;
import cn.aitrox.ry.app.web.service.ComponentService;
import cn.aitrox.ry.app.web.service.LesionService;
import cn.aitrox.ry.app.web.service.TaskService;
import cn.aitrox.ry.app.web.service.UserService;
import cn.aitrox.ry.app.web.vo.anno.result.out.AnnoResultUsersOutVo;
import cn.aitrox.ry.bean.RespResult;
import cn.aitrox.ry.common.exception.CustomBizException;
import cn.aitrox.ry.common.service.BaseService;
import cn.aitrox.ry.common.service.api.dto.User;
import cn.aitrox.ry.constant.CommonConstant;
import cn.aitrox.ry.service.anno.dto.anno.result.in.AnnoResultExportInDto;
import cn.aitrox.ry.service.anno.dto.anno.result.in.AnnoResultReadInDto;
import cn.aitrox.ry.service.anno.dto.anno.result.in.AnnoResultSubmitInDto;
import cn.aitrox.ry.service.anno.dto.anno.result.in.AnnoSeriesGetInDto;
import cn.aitrox.ry.service.anno.dto.anno.result.in.AnnoUsersInDto;
import cn.aitrox.ry.service.anno.dto.anno.result.in.AnnoYayInDto;
import cn.aitrox.ry.service.anno.dto.anno.result.in.DeepzoomUrlInDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.AnnoResultDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.AnnoResultExportResultOutDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.DeepzoomUrlOutDto;
import cn.aitrox.ry.service.anno.dto.anno.result.out.SeriesGetResultDto;
import cn.aitrox.ry.service.anno.dto.task.TaskDto;
import cn.aitrox.ry.service.anno.entity.AnnoComponentResultEntity;
import cn.aitrox.ry.service.anno.entity.AnnoLesionResultEntity;
import cn.aitrox.ry.service.anno.entity.AnnoLesionResultGroupEntity;
import cn.aitrox.ry.service.anno.entity.AnnoResultEntity;
import cn.aitrox.ry.service.anno.enumtype.anno.AnnoResultStatusEnum;
import cn.aitrox.ry.service.anno.enumtype.anno.LesionResultTypeEnum;
import cn.aitrox.ry.service.anno.enumtype.anno.YayEnum;
import cn.aitrox.ry.service.anno.feign.AnnoResultFeign;
import cn.aitrox.ry.service.config.dto.ComponentDto;
import cn.aitrox.ry.service.config.dto.LesionDto;
import cn.aitrox.ry.service.oss.dto.FileRestoreInfoDto;
import cn.aitrox.ry.service.oss.dto.in.DowanloadUrlGetInDto;
import cn.aitrox.ry.service.oss.feign.OssFeign;
import cn.aitrox.ry.util.ArrayUtil;
import cn.aitrox.ry.util.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class AnnoResultServiceImpl extends BaseService implements AnnoResultService {

    public static final String NII_GZ = "nii.gz";
    public static final String NII_GZ_GEN_MSG = "nii.gz正在生成中";

    @Autowired
    @Lazy
    private AnnoResultFeign annoResultFeign;

    @Autowired
    @Lazy
    private OssFeign ossFeign;

    @Autowired
    private ComponentService componentService;

    @Autowired
    private LesionService lesionService;

    @Autowired
    private UserService userService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private AppNacosProperties appNacosProperties;

    public RespResult<SeriesGetResultDto> seriesGet(AnnoSeriesGetInDto input) {
        return annoResultFeign.seriesGet(input);
    }

    public RespResult<AnnoResultDto> read(AnnoResultReadInDto input) {
        return annoResultFeign.read(input);
    }

    public RespResult yay(AnnoYayInDto input) {
        return annoResultFeign.yay(input);
    }

    public RespResult submit(AnnoResultSubmitInDto input) {
        return annoResultFeign.submit(input);
    }

    public RespResult<AnnoResultUsersOutVo> annoUsers(AnnoUsersInDto input) {
        RespResult<List<Integer>> respResult = annoResultFeign.annoUsers(input);
        if (!respResult._isOk()) {
            return RespResult.secByError(respResult.getCode(), respResult.getMsg());
        }
        List<Integer> userIds = respResult.getData();
        if (ArrayUtil.isEmpty(userIds)) {
            return RespResult.ok();
        }
        List<User> userList = userService.list(userIds);
        userList = userList.stream().map(x -> {
            User user = new User();
            user.setId(x.getId());
            user.setUserName(x.getUserName());
            return user;
        }).collect(Collectors.toList());
        return RespResult.ok(new AnnoResultUsersOutVo(userList));
    }

    @Override
    public Map<Integer, List<List<List<String>>>> export(List<Integer> taskIds) {
        if (ArrayUtil.isEmpty(taskIds)) {
            return new HashMap<>();
        }
        AnnoResultExportResultOutDto exportResult = annoResultFeign.export(new AnnoResultExportInDto(taskIds));
        List<AnnoResultEntity> annoResultList = exportResult.getAnnoResultList();
        if (ArrayUtil.isEmpty(annoResultList)) {
            return new HashMap<>();
        }
        List<AnnoLesionResultEntity> annoLesionResultList = exportResult.getAnnoLesionResultList();
        List<String> maskFilePathList = annoLesionResultList.stream().filter(x -> StringUtils.isNotEmpty(x.getMaskFilePath())).map(AnnoLesionResultEntity::getMaskFilePath).collect(Collectors.toList());
        RespResult<List<FileRestoreInfoDto>> dowanloadUrlGetResult = ossFeign.urlGet(new DowanloadUrlGetInDto(maskFilePathList, appNacosProperties.getMaskUrlLanExpire(), appNacosProperties.getStorageType()));
        if (!dowanloadUrlGetResult._isOk()) {
            throw new CustomBizException("生成Mask下载地址失败");
        }
        Map<String, FileRestoreInfoDto> maskFileStoreInfoMap = dowanloadUrlGetResult.getData().stream().collect(Collectors.toMap(FileRestoreInfoDto::getResourceKey, x -> x));
        List<AnnoComponentResultEntity> annoComponentResultList = exportResult.getAnnoComponentResultList();

        Map<Integer, List<List<Integer>>> taskCompIdsMap = new HashMap<>();
        for (Integer taskId : taskIds) {
            List<List<Integer>> taskSettingComIds = taskService.getTaskSettingComIds(taskId);
            taskCompIdsMap.put(taskId, taskSettingComIds);
        }

        List<TaskDto> taskList = taskService.getTaskList(taskIds);
        Map<Integer, TaskDto> taskMap = taskList.stream().collect(Collectors.toMap(TaskDto::getId, x -> x));
        Map<Integer, ComponentDto> allCompoentMap = componentService.getAllCompoentMap();
        Map<String, LesionDto> lesionMap = lesionService.getAllLesionMap();

        List<Integer> userIdList = annoResultList.stream().map(AnnoResultEntity::getUserId).distinct().collect(Collectors.toList());
        List<User> userList = userService.list(userIdList);
        Map<Integer, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, x -> x));


        annoLesionResultList = null == annoLesionResultList ? new ArrayList<>() : annoLesionResultList;
        annoComponentResultList = null == annoComponentResultList ? new ArrayList<>() : annoComponentResultList;

        Map<Integer, List<AnnoResultEntity>> taskAnnoResultMap = annoResultList.stream().collect(Collectors.groupingBy(AnnoResultEntity::getTaskId));
        Map<Integer, List<AnnoLesionResultEntity>> taskAlrsMap = annoLesionResultList.stream().collect(Collectors.groupingBy(AnnoLesionResultEntity::getArId));
        Map<Integer, Map<Integer, List<AnnoComponentResultEntity>>> arIdAlrIdAcrsMap = annoComponentResultList.stream().collect(Collectors.groupingBy(AnnoComponentResultEntity::getArId, Collectors.groupingBy(AnnoComponentResultEntity::getAlrId)));

        Map<Integer, List<List<List<String>>>> result = new HashMap<>();

        for (Integer taskId : taskIds) {
            List<AnnoResultEntity> arListByTask = taskAnnoResultMap.get(taskId);
            if (null == arListByTask) {
                result.put(taskId, Arrays.asList(new ArrayList<>(), new ArrayList<>()));
                continue;
            }
            List<List<Integer>> compIdsByTask = taskCompIdsMap.get(taskId);
            Map<Integer, ComponentDto> compMapByTask = new HashMap<>();
            for (List<Integer> compIds : compIdsByTask) {
                for (Integer compId : compIds) {
                    compMapByTask.put(compId, allCompoentMap.get(compId));
                }
            }

            List<List<List<String>>> exportDataByTask = this.packExportData4Task(arListByTask, taskAlrsMap, arIdAlrIdAcrsMap, taskMap, compMapByTask, compIdsByTask, lesionMap, userMap, maskFileStoreInfoMap, exportResult.getAnnoLesionResultGroupList());
            result.put(taskId, exportDataByTask);
        }
        return result;
    }

    @Override
    public RespResult<DeepzoomUrlOutDto> deepzoomUrl(DeepzoomUrlInDto input) {
        return annoResultFeign.deepzoomUrl(input);
    }

    private List<List<List<String>>> packExportData4Task(List<AnnoResultEntity> annoResultList, Map<Integer, List<AnnoLesionResultEntity>> taskAlrsMap, Map<Integer, Map<Integer, List<AnnoComponentResultEntity>>> arIdAlrIdAcrsMap, Map<Integer, TaskDto> taskMap, Map<Integer, ComponentDto> taskCompoentMap, List<List<Integer>> taskCompIds, Map<String, LesionDto> lesionMap, Map<Integer, User> userMap, Map<String, FileRestoreInfoDto> maskFileStoreInfoMap, List<AnnoLesionResultGroupEntity> alrGroupList) {
        List<List<List<String>>> result = new ArrayList<>();
        List<List<String>> annoResultExportData = this.packExportData4AnnoResult(annoResultList, taskAlrsMap, arIdAlrIdAcrsMap, taskMap, taskCompoentMap, taskCompIds, lesionMap, userMap, maskFileStoreInfoMap, alrGroupList);
        List<List<String>> componentExportData = this.packComponentExportData(taskCompoentMap);

        result.add(annoResultExportData);
        result.add(componentExportData);

        return result;

    }

    private List<List<String>> packExportData4AnnoResult(List<AnnoResultEntity> annoResultList, Map<Integer, List<AnnoLesionResultEntity>> taskAlrsMap, Map<Integer, Map<Integer, List<AnnoComponentResultEntity>>> arIdAlrIdAcrsMap, Map<Integer, TaskDto> taskMap, Map<Integer, ComponentDto> taskCompoentMap, List<List<Integer>> taskComIds, Map<String, LesionDto> lesionMap, Map<Integer, User> userMap, Map<String, FileRestoreInfoDto> maskFileStoreInfoMap, List<AnnoLesionResultGroupEntity> alrGroupList) {
        if (ArrayUtil.isEmpty(annoResultList)) {
            return null;
        }
        List<List<String>> result = new ArrayList<>();
        List<String> header = new ArrayList<>();
        result.add(header);
        List<String> hardHeader = Arrays.asList("任务ID", "任务名称", "用户手机号", "用户ID", "影像结果编号", "序列号", "开始时间", "提交时间", "是否废片", "自定义内容", "阴阳性", "病灶", "病灶名称", "影像工具", "影像结果类型", "影像结果", "文件类型", "病灶组结果");
        header.addAll(hardHeader);

        Map<Integer, Integer> imageCompIdIndexMap = this.addCompResultHeader(header, taskComIds.get(1), taskCompoentMap);
        // 空列区分大小征象
        header.add("");
        Map<Integer, Integer> seriesCompIdIndexMap = this.addCompResultHeader(header, taskComIds.get(0), taskCompoentMap);

        List<Integer> seriesImageCompIds = new ArrayList<>();
        seriesImageCompIds.addAll(taskComIds.get(0));
        seriesImageCompIds.addAll(taskComIds.get(1));
        Map<Integer, Map<String, String>> compDataParseMap = seriesImageCompIds.stream().distinct().collect(Collectors.toMap(x -> x, x -> {
            return componentService.parseData2ValNameMap(taskCompoentMap.get(x));
        }));


        Map<String, List<AnnoResultEntity>> seriesArsMapByTask = annoResultList.stream().collect(Collectors.groupingBy(AnnoResultEntity::getSeriesUid));
        for (String seriesUid : seriesArsMapByTask.keySet()) {
            List<AnnoResultEntity> annoResultsBySeries = seriesArsMapByTask.get(seriesUid);
            for (AnnoResultEntity annoResult : annoResultsBySeries) {
                Map<Integer, List<AnnoComponentResultEntity>> seriesImageAcrMap = arIdAlrIdAcrsMap.get(annoResult.getId());
                seriesImageAcrMap = null == seriesImageAcrMap ? new HashMap<>() : seriesImageAcrMap;
                List<AnnoLesionResultEntity> alrList = taskAlrsMap.get(annoResult.getId());
                if (ArrayUtil.isEmpty(alrList)) {
                    List<String> rowData = this.packRowData4SeriesResult(header.size(), annoResult, taskMap, userMap);
                    this.setRowData4CompResult(rowData, seriesImageAcrMap.get(0), seriesCompIdIndexMap, compDataParseMap, taskCompoentMap);
                    result.add(rowData);
                    continue;
                }
                for (AnnoLesionResultEntity alr : alrList) {
                    AnnoLesionResultGroupEntity group = null;
                    if (alr.getGroupId() != null && alr.getGroupId() > 0) {
                        group = alrGroupList.stream().filter(groupItem -> groupItem.getId().equals(alr.getGroupId())).findFirst().orElse(null);
                    }
                    List<String> rowData = this.packRowData4SeriesResult(header.size(), annoResult, taskMap, userMap);
                    this.setRowData4CompResult(rowData, seriesImageAcrMap.get(0), seriesCompIdIndexMap, compDataParseMap, taskCompoentMap);
                    this.setRowData4CompResult(rowData, seriesImageAcrMap.get(1), imageCompIdIndexMap, compDataParseMap, taskCompoentMap);
                    this.setRowData4ImageResult(rowData, alr, maskFileStoreInfoMap, lesionMap, group);
                    result.add(rowData);
                }
            }
        }

        return result;

    }

    private void setRowData4ImageResult(List<String> rowData, AnnoLesionResultEntity alr, Map<String, FileRestoreInfoDto> maskUrlMap, Map<String, LesionDto> lesionMap, AnnoLesionResultGroupEntity group) {
        rowData.set(4, alr.getId().toString());
        rowData.set(11, alr.getLesion());
        rowData.set(12, lesionMap.get(alr.getLesion()).getName());
        rowData.set(13, alr.getTool());
        rowData.set(14, LesionResultTypeEnum._getEnum(alr.getResultType()).getDesc());
        rowData.set(15, LesionResultTypeEnum._isMask(alr.getResultType()) ? (StringUtils.isEmpty(alr.getMaskFilePath()) ? NII_GZ_GEN_MSG : maskUrlMap.get(alr.getMaskFilePath()).getUrlLan()) : alr.getResult());
        rowData.set(16, LesionResultTypeEnum._isMask(alr.getResultType()) ? NII_GZ : CommonConstant.EMPTY);
        rowData.set(17, null == group ? CommonConstant.EMPTY : group.getInfo());


        return;
    }

    private void setRowData4CompResult(List<String> rowData, List<AnnoComponentResultEntity> acrList, Map<Integer, Integer> compIdIndexMap, Map<Integer, Map<String, String>> compDataParseMap, Map<Integer, ComponentDto> componentMap) {
        if (ArrayUtil.isEmpty(acrList)) {
            return;
        }
        for (AnnoComponentResultEntity acr : acrList) {
            Integer rowIndex = compIdIndexMap.get(acr.getComponentId());
            rowData.set(rowIndex, acr.getResult());
            rowData.set(rowIndex + 1, componentService.getComponentText(componentMap.get(acr.getComponentId()).getType(), compDataParseMap.get(acr.getComponentId()), acr.getResult()));// Mapping
        }
    }

    private List<String> packRowData4SeriesResult(Integer headerSize, AnnoResultEntity annoResult, Map<Integer, TaskDto> taskMap, Map<Integer, User> userMap) {
        String taskIdStr = annoResult.getTaskId().toString();
        String taskName = taskMap.get(annoResult.getTaskId()).getName();
        String mobile = userMap.get(annoResult.getUserId()).getMobile();
        String userIdStr = annoResult.getUserId().toString();
        String beginTime = DateUtil.formatTime(annoResult.getBeginTime());
        String submitTime = DateUtil.formatTime(annoResult.getSubmitTime());
        AnnoResultStatusEnum statusEnum = AnnoResultStatusEnum.getEnumByStatus(annoResult.getStatus());
        String isDiscard = statusEnum == AnnoResultStatusEnum.DISCARD ? "是" : "否";
        String customData = CommonConstant.EMPTY;
        YayEnum yayEnum = YayEnum.getEnumByValue(annoResult.getYay());
        String yay = null == yayEnum ? CommonConstant.EMPTY : yayEnum.getDesc();

        List<String> rowData = new ArrayList<>();
        for (int i = 0; i < headerSize; i++) {
            rowData.add(CommonConstant.EMPTY);
        }
        rowData.set(0, taskIdStr);
        rowData.set(1, taskName);
        rowData.set(2, mobile);
        rowData.set(3, userIdStr);
        rowData.set(5, annoResult.getSeriesUid());
        rowData.set(6, beginTime);
        rowData.set(7, submitTime);
        rowData.set(8, isDiscard);
        rowData.set(9, customData);
        rowData.set(10, yay);
        return rowData;
    }

    private Map<Integer, Integer> addCompResultHeader(List<String> header, List<Integer> compIds, Map<Integer, ComponentDto> taskCompoentMap) {
        Map<Integer, Integer> result = new HashMap<>();
        if (ArrayUtil.isEmpty(compIds)) {
            return result;
        }
        for (Integer compId : compIds) {
            ComponentDto component = taskCompoentMap.get(compId);
            header.add(component.getName());
            header.add(component.getName() + " - Mapping");
            result.put(compId, header.size() - 2);
        }
        return result;
    }

    private List<List<String>> packComponentExportData(Map<Integer, ComponentDto> compoentMap) {
        if (ArrayUtil.isEmpty(compoentMap)) {
            return null;
        }
        List<List<String>> result = new ArrayList<>();
        result.add(Arrays.asList("编码", "名称", "选项数据"));
        for (Integer comId : compoentMap.keySet()) {
            ComponentDto comp = compoentMap.get(comId);
            result.add(Arrays.asList(comId.toString(), comp.getName(), comp.getData()));
        }
        return result;
    }
}
